package com.wquick.base.utils.downTime

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.transform

//Thread.sleep(1000)有时候会将线程睡眠1016毫秒之多,而这些是跟操作系统,编程语言,CPU线程相关的,我们几乎无法改变,ps:且执行逻辑可能也会占用时间
//这也就导致了,如果你的循环要跑几个月的话(后端程序很正常),每次循环多个几毫秒,这样累加起来可能任务就会少执行很多次,且执行的时间点也会越偏缺远
//我们可以使用自校准的方式,来使任务执行次数和时间尽量少出(或不出)偏差
//阻塞,异步或挂起自校准方案
//我们可以每次在执行逻辑和延时的时候记录当前使用了多少时间(多用了多少时间),然后在下次延时的时候少延时相应时间,这样就可以消除其时间偏差
//
//    while (true) {
//        //记录开始时间
//        //执行逻辑
//        delay(1000 - 多用的时间)//延时1秒
//        //记录执行逻辑和延时多用了多长时间
//    }
//
//这个方案很好的解决了时间偏差的问题,但其实也有如下一些问题:
//比如每1秒执行一次,但我的执行逻辑就用了一秒多,这就可能会出现问题了
//或者如果此时cpu(操作系统)睡眠了,导致十秒没有cpu时间片,这样就会丢失10个事件
//响应式自校准方案
//我们可以使用Flow来做响应式的自校准方案
//

/**
 * 延时准确的循环回调flow
 * [timeInterval]时间间隔
 * [callNow]是否执行时就先发送一次事件
 */
fun downtimeFlow(timeInterval: Long, callNow: Boolean = true) =
    flow {
        val startTime = System.currentTimeMillis()//@1
        var i = if (callNow) 0 else 1
        while (true) {
            emit(startTime + i * timeInterval)//@2
            i++
        }
    }.buffer(10)//@3
        .transform {
            //delay到指定时间发送,如果因cpu睡眠等原因导致超过了时间,则直接发送(delay内负数会直接放行)
            delay(it - System.currentTimeMillis())//@4
            emit(it)
        }.flowOn(Dispatchers.Default)//@5
